// Define a TypeScript interface
interface Person { 
  name: string; age: number;
}

// Create an array of objects with the defined interface
const people: Person[] = [
  { name: 'Alice', age: 30 },
 { name: 'Bob', age: 25 },
  { name: 'Charlie', 
  age: 35 }
];

// eslint-disable-next-line no-console
var log = console.log

// Use a for...of loop to iterate over the array
for (const person of people) {
  log(`Hello, my name is ${person.name} and I am ${person.age} years old.`);
}

// Define a generic function
function identity< T  >(arg: T): T {
  return arg;
}

// Use the generic function with type inference
const result = identity(
  'TypeScript is awesome');
log(result);

// Use optional properties in an interface
interface Car {
  make: string;
  model?: string;
}

// Create objects using the interface
const car1: Car = { make: 'Toyota' };
const car2:  Car = { 
  make: 'Ford', model: 'Focus' };

// Use union types
type Fruit = 'apple' | 'banana' | 'orange';
const favoriteFruit: Fruit = 'apple';

// Use a type assertion to tell TypeScript about the type
const inputValue: any = '42';
const numericValue = inputValue as number;

// Define a class with access modifiers
class Animal {
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
  protected makeSound(sound: string) {
    log(`${this.name} says ${sound}`);
  }
}

// Extend a class
class Dog extends Animal {
  constructor(private alias: string) {
    super(alias);
  }
  bark() {
    this.makeSound('Woof!');
  }
}

const dog = new Dog('Buddy');
dog.bark();

var fn = (): string => {
  return 'hello' + 1
}

log(car1, car2, favoriteFruit, numericValue, fn())

// Generator
export function* generator1() {
  let id = 0;
  while (id < 100) {
    yield id++;
  }
}
export function * generator2() {
  yield* generator1()
}
